
	/***********************************************************************************************

		@API
		Module: Jsl

		Base:
			mod			require						(libs..)
			mod			load							(urls..,options*)
			cn			request						(url,		options*)
			cn			connect						(url, 	options*)

			obj			object						(oid)
			mod			augment						(types..)
			v				execute						(script)

		Timers:
			tid			delay							(action, time*)
			tid			timer							(action, interval, n*)
			mod			removeTimer				(tid)

		System messages:
			msg			message						(id)
			mod			message						(id, msg, locale*)
			msg			message						(id, params*)

		Errors:
			mod			error							(name, super*, message*)
							throwError				(e, cx*, args*)
			mod			captureError			(e, cx*, args*)

		Introspection:
			b				isAugmented				(v)
			b				isArray						(v)
			b				isArrayLike				(v)
			b				isBoolean					(v)
			b				isClass						(v)
			b				isDate						(v)
			b				isError						(v)
			b				isFunction				(v)
			b				isInteger					(v)
			b				isInterface				(v)
			b				isModule					(v)
			b				isNumber					(v)
			b				isObject					(v)
			b				isRegExp					(v)
			b				isString					(v)
			b 			isSubTypeOf				(type, type2)
			b 			isSuperTypeOf			(type, type2)

		Debug:
			mod			validator					(id, rule)
			mod			validate					(v, id, param*, error*)

		Objects:
			console
			eventBus

		Infos:
			app					{ url	}
			liquid			{ url, mode, log, version }
			javascript	{ version }
			system			{ id, type, nature, version, versionId, os, context }

		Events:
			beforeRun, beforeExit, beforeRequire, beforeLoad, beforeRequest, beforeConnect, beforeLog, beforeError
			run, exit, require, load, request, connect, log, error

		Note:
			action				-> f(tid, I+)
			liquid.log		-> none, error, warn, info, debug
			system.type		-> web, native
			system.nature	-> desktop, mobile

	************************************************************************************************/

	//Module init
	//-------------------------------------------------------------------------------
	;(function() {
		try {
			var mod, id, lbs, ua, url, lqd, res

			//init
			//-------------------------------------------------------------------------------
			jsl.__log		= "debug"
      jsl.__mods	= []
      jsl.__libs	= {}
			jsl.__objs	= {}

			jsl.__hld		= 0
			jsl.__ready = 0

			mod	= new jsl.Module("jsl")
			for(id in jsl) mod[id] = jsl[id]

      jsl =	mod
			jsl.__ni_init()

      //libs
			//-------------------------------------------------------------------------------
			lbs = jsl.__libs
			lbs.crypto 			= true
			lbs.data 				= true
			lbs.datasource 	= true
			lbs.file				= true
			lbs.lang 				= true
			lbs.locale 			= true
			lbs.storage 		= true
			lbs.timer 			= true
			lbs.ui 					= true

			lbs.ncrypto 		= "Crypto"
			lbs.ndata 			= "Data"
			lbs.ndatasource	= "DataSource"
			lbs.nfile				= "-"
			lbs.nlang 			= "-"
			lbs.nlocale 		= "Locale"
			lbs.nstorage 		= "Storage"
			lbs.ntimer 			= "Timer"
			lbs.nui 				= "Ui"

			//event bus
			//-------------------------------------------------------------------------------
			jsl.eventBus = new jsl.EventMap
			jsl.eventBus.__name = "jsl.eventBus"

      //Events
			//-------------------------------------------------------------------------------
			jsl.events(	"beforeRun", "beforeExit", "beforeError", "beforeRequire", "beforeLoad", "beforeRequest", "beforeConnect", "beforeLog",
									"run", "exit", "error", "require", "load", "request", "connect", "log")

			//URL roots
			url = jsl.Url(jsl.__ni_inf_url_app())
			lqd	= jsl.Url(jsl.__ni_inf_url_lqd())
			res = url.resource()

			jsl.Url.scheme("app", 		res && res.match(/\.[^.]+$/) ? url.parent() : url)
			jsl.Url.scheme("liquid",	lqd.parent())

			jsl.__log = (lqd.params().log || "debug").toLowerCase()

			//Infos
			//-------------------------------------------------------------------------------
			jsl.info("app"								, {})
			jsl.info("app.url"						, function() { return jsl.Url(url) })

			jsl.info("liquid"							, {})
			jsl.info("liquid.url"					, function() { return jsl.Url(lqd) })
			jsl.info("liquid.mode"				, jsl.__dbg ? "debug" : "release")
			jsl.info("liquid.log"					, jsl.__log)
      jsl.info("liquid.version"			, jsl.__v)

			jsl.info("javascript", {})
      jsl.info("javascript.version"	, jsl.__ni_inf_jsv())

			ua = jsl.__ni_inf_ua()
			jsl.info("system"							,	{})
			jsl.info("system.id"					,	jsl.__sys = ua.id)
			jsl.info("system.version"			, ua.v)
			jsl.info("system.versionId"		, ua.vid)
			jsl.info("system.os"			  	, jsl.__ni_inf_os())
			jsl.info("system.type"				, jsl.__ni_inf_sys())
			jsl.info("system.nature"			, jsl.__ni_inf_dev())
			jsl.info("system.context"			,	jsl.__go)

			//-------------------------------------------------------------------------------
			jsl.eventAction("error", function(e) {
				jsl.console.error((e.error + "").replace(/\$/g, "\\$"))
			})

			//-------------------------------------------------------------------------------
			jsl.Connector.bind("send", 		{ id: "__1", action: function(e) { if(e.connector.url().protocol() === "http") jsl.__hld++ }})
			jsl.Connector.bind("receive", { id: "__2", action: function(e) { if(e.connector.url().protocol() === "http") jsl.__hld-- }})
			jsl.Connector.bind("error", 	{ id: "__3", action: function(e) { if(e.connector.url().protocol() === "http") jsl.__hld-- }})

			jsl.__ready = 1
		}
		catch(e) {
			throw Error("Liquid(error) >> " + e)
		}
	})()
	//-------------------------------------------------------------------------------
	jsl.__run = function() {
		try {
			var e, b

			e 						= new jsl.Event
			e.app 				= jsl.info("app")
			e.system 			= jsl.info("system")
			e.javascript	= jsl.info("javascript")
			e.liquid 			= jsl.info("liquid")

			if(!jsl.__ready)
				jsl.console.error(jsl.message("e.SystemError"))

			else {
				b = jsl.throwEvent("beforeRun", e).sequence

				if(b)
					jsl.delay(function() {
						var obj, id, Cn

						//1-waiting requests
						if(jsl.__hld)
							jsl.delay(arguments.callee, 100)

						//2-system ready to run
						else {

							obj = {}
							for(id in obj)
								throw jsl.MisuseError("BuiltInObjectAugmented")

							jsl.console.info("sys.LiquidReady", 		jsl.__v, 			e.liquid.mode)
							jsl.console.info("sys.SystemDetected", 	e.system.id,	e.system.versionId)

							Cn = jsl.Connector
							Cn.unbind("__1")
							Cn.unbind("__2")
							Cn.unbind("__3")

							jsl.console.info("event: run")
							jsl.throwEvent("run", e)
						}
					}, 0)
			}
		}
		catch(e) {
			jsl.captureError(e, "jsl.run")
		}
	}
	//-------------------------------------------------------------------------------
	jsl.__exit = function() {
		try {
			jsl.throwEvent("beforeExit")

			//@sys: Explorer 7 - throw an error when the page is exited and we try to print
			//			to the console
			try				{ jsl.console.info("event: exit") }
			catch(e) 	{}

			jsl.throwEvent("exit")
		}
		catch(e) {
			jsl.captureError(e, "jsl.exit")
		}
	}
	//-------------------------------------------------------------------------------
	jsl.require = function() {
		try {
			var url, lbs, e, a, l, id, surl, s

			//!
			if(!arguments.length)	throw jsl.ParamError()//!

			url = this.info("liquid.url") + "/lib/"
			lbs = this.__libs
			e 	= new jsl.Event

			for(a = 0, l = arguments.length; a < l; a++) {

				id = arguments[a]//!
				this.validate(id, "s+", "libs[" + a + "]")//!

				id = id.toLowerCase()//!
				if(!lbs[id]) throw jsl.ParamError("LibraryNotDefined", [ id ])//!

				if(!(lbs[id] instanceof jsl.Module)) {

					surl	= jsl.Url(url + id + ".js")
					e.id 	= id
					e.url = surl
					this.throwEvent("beforeRequire", e)

					//?v= -> anti-cache
					if(lbs[id] !== true) s = lbs[id]
					else s = this.Connector(surl + "?v=" + jsl.__v).set({ async: false, content: "text" }).send()

					if(!s.search(/\s*\/\*[\s\S]+?\*\/\s*jsl\.__libs/)) {
						this.__ni_jvs_exec(s)
						s = lbs[id]
					}

					if(!this.__dbg)
						s = s.replace(/\/\*!\*\/[\s\S]+?\/\*!\*\//g	,	"").
									replace(/\/\/![\s\S]+?\/\/!.*\n/g			, "")

					try 			{	this.__ni_jvs_exec(s)	}
					catch(e) 	{ throw jsl.Error(e).trace("script: " + surl)	}

					lbs[id] = jsl[lbs[ "n" + id ]] || jsl
					this.throwEvent("require", e)
					jsl.console.info("event: require(" + surl + ")")
				}
			}

			return lbs[id]
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
	//-------------------------------------------------------------------------------
	jsl.load = function() {
		try {
			var ls, l, os, i, cns, e, fn, id, a, url, cn, es, b, as, fp, tp, ob, mid//!
			if(!arguments.length)	throw jsl.ParamError()//!

			ls 	= Array.prototype.slice.call(arguments)
			l		= ls.length
			os 	= ls[l - 1]

			tp	= typeof os
			as	= tp === "boolean"
			fp	= tp === "function"
			ob	= tp === "object"

			if(fp || as) {
				os = {}
				if(as) 			os.async 			= !!+ls[--l]
				else if(fp)	os.onReceive 	= ls[--l]
			}

			else if(l === 1 || jsl.Url.isClassOf(os) || jsl.isString(os))
				os = {}

			else {//!
				jsl.validate(os, "o", "args[" + (l - 1) + "]")
				if(os.jsonp		!= null)																						throw jsl.ParamError("NotRequired:options.jsonp")
				if(os.content != null)																						throw jsl.ParamError("NotRequired:options.content")
				if(jsl.isString(os.method) && !os.method.match(/^(get|post)$/i))	throw jsl.ParamError("BadValue:options.method")//!
				l--
			}

			i		= 0
			cns = []
			e 	= new jsl.Event
			os.content = "text"
			//---------------------------------------------------
			fn 	= function(e) {
				var s, b

				for(b = 0; b < cns.length; b++)
					if(cns[b].connector === this)
						break;

				cns[b].script = e.data || null
				i++

				if(i === l) {

					if(os.onBeforeReceive) {
						e.eventId = "beforeReceive"
						e.data 		= null
						try { os.onBeforeReceive.call(jsl, e) }
						catch(e) { jsl.throwError(e, "beforeReceive(" + url + ")") }
					}

					for(b = 0; b < cns.length; b++)
						if(cns[b].script !== null) {

							e.connector		= null
							e.data 				= null
							cns[b].status = "loaded"
							jsl.throwEvent("beforeLoad", e)

							try 			{	jsl.execute(cns[b].script)		}
							catch(e) 	{ jsl.throwError(e.script(url)) }

							e.connector	= cns[b].connector
							e.data 			= cns[b].script
							jsl.console.info("event: load(" + e.connector.url() + ")")
							jsl.throwEvent("load", e)
						}
						else
							cns[b].status = "error"

					if(os.onReceive) {
						delete e.connector
						e.eventId = "receive"
						e.data		= cns
						try { os.onReceive.call(jsl, e) }
						catch(e) { jsl.throwError(e, "receive(" + url + ")") }
					}
				}
			}

			//---------------------------------------------------
			es = this.Connector.events()
			for(a = 0; a < l; a++) {

				url = ls[a]//!
				jsl.validate(url, "url", "urls[" + a + "]")//!

				cn = new jsl.Connector(url)
				cn.set(os)

				if(ob || fp)
					for(b = 0; b < es.length; b++) {
						id 	= es[b]
						mid = "on" + id.charAt(0).toUpperCase() + id.slice(1)
						if(os[mid] != null) {//!
							jsl.validate(os[mid], "f", "options." + id)//!
							if(id !== "beforeReceive" && id !== "receive") cn.bind(id, os[mid])
						}
					}

				cns.push({ connector: cn })
				cn.bind("receive", 	fn)
				cn.bind("error",		fn)
				cn.send()
			}
			return this
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
	//-------------------------------------------------------------------------------
	jsl.request = function(url, options) {
		var cn, id, os, es, fn, sr, as, tp, mid//!
		if(!arguments.length || arguments.length > 2)	throw jsl.ParamError()
		jsl.validate(url, "url")
		if(jsl.Url(url).protocol() === "ws")	throw jsl.ParamError("BadUrlScheme:url:http, https, file")
		if(arguments.length === 2)						jsl.validate(options, "t(o,s+,b)", "options")//!

		tp = typeof options
		fn = tp === "function"
		sr = tp === "string"
		as = tp === "boolean"

		os = fn || sr || as || !options ? {} : options//!
		if(os.id != null)	jsl.validate(os.id, "s+", "options.id")//!

		if(os.id != null)	cn = new this.Connector(url, os.id)
		else							cn = new this.Connector(url)

		if(fn) 			os.onReceive 	= options
		else if(sr)	os.content		= options
		else if(as) os.async			= !!+options

		if(os.async == null && !os.onReceive && !os.onBeforeReceive)
			os.async = false

		cn.set(os)
		if(options && !sr && !as) {
			es = cn.events()
			for(var a = 0, l = es.length; a < l; a++) {

				id 	= es[a]
				mid = "on" + id.charAt(0).toUpperCase() + id.slice(1)

				if(os[mid] != null) {//!
					jsl.validate(os[mid], "f", "options." + id)//!
					cn.bind(id, os[mid])
				}
			}
		}

		cn.bind("beforeReceive", 	function(e) { jsl.throwEvent("beforeRequest", e) })
		cn.bind("receive", 				function(e) { jsl.throwEvent("request", e); jsl.console.info("event: request(" + jsl.Url(url) + ")")  })
		return cn.send(os.data)
	}
	//-------------------------------------------------------------------------------
	jsl.connect = function(url, options) {
		var cn, id, os, es, mid, tp//!
		if(!arguments.length || arguments.length > 2)	throw jsl.ParamError()
		jsl.validate(url, "url")
		if(jsl.Url(url).protocol() !== "ws")	throw jsl.ParamError("BadUrlScheme:url:ws, wss")
		if(arguments.length === 2)						jsl.validate(options, "t(o,s+)", "options")//!

		tp = typeof options

		if(tp === "function")			os = { onReceive: options }
		else if(tp === "string")	os = { protocol: options 	}
		else											os = options || {}

		//!
		if(os.id != null)	jsl.validate(os.id, "s+", "options.id")//!

		if(os.id != null)	cn = new this.Connector(url, os.id)
		else							cn = new this.Connector(url)

		cn.set(os)
		if(options) {
			es = cn.events()
			for(var a = 0, l = es.length; a < l; a++) {

				id 	= es[a]
				mid = "on" + id.charAt(0).toUpperCase() + id.slice(1)

				if(os[mid] != null) {//!
					jsl.validate(os[mid], "f", "options." + id)//!
					cn.bind(id, os[mid])
				}
			}
		}

		cn.bind("beforeOpen",	function(e) { jsl.throwEvent("beforeConnect", e) 	})
		cn.bind("open", 			function(e) { jsl.throwEvent("connect", e); jsl.console.info("event: connect(" + jsl.Url(url) + ")") })

		cn.open()
		return cn
	}
	//-------------------------------------------------------------------------------
	jsl.object = function(oid) {
		try {//!
			if(arguments.length !== 1)	throw jsl.ParamError()
			jsl.validate(oid, "s+", "oid")//!
			return this.__objs[oid] || null
		}
		catch(e) {
  		jsl.throwError(e, this, arguments)
		}
	}
	//-------------------------------------------------------------------------------
	jsl.augment = function()  {
		try {
			var args, arg, len, a, b, go, id

			var bts = [ Array		, Boolean		, Date	, Function	, Number	, String,	jsl.__go 	]
			var ids = [ "Array"	, "Boolean"	, "Date", "Function", "Number", "String" 					]

			args 	= arguments
			len 	= args.length
			arg		= args[0]

			//!
			if(!len)	throw jsl.ParamError()//!

			//1-*
			if(arg === "*")
				this.augment.apply(this, bts)

			//2-constructors list
			else
				for(a = 0; a < len; a++) {

					arg	= args[a]

					//!
					if(arg === Object) throw jsl.ParamError("OperationDenied")//!
					for(b = 0; b < bts.length; b++)
						if(arg === bts[b])
							break

					//!
					if(b === bts.length)	throw jsl.ParamError("BadType:arg[" + a + "]")//!
					id = ids[b]

					//1-built-in type
					if(id) {
						if(!jsl[id]) jsl.require("lang")
						jsl.Object.augment(arg.prototype, jsl[id].prototype)
						jsl.Object.augment(arg, jsl[id])
					}

					//2-global object
					else {

						go = jsl.__go
						go.lconsole = jsl.console

						//on the global scope we can have name collisions with File, console etc..
						if(this.Locale)			go.lLocale			= this.Locale
						if(this.Crypto)			go.lCrypto 			= this.Crypto
						if(this.Storage)		go.lStorage			= this.Storage
						if(this.Dir)				go.lDir					= this.Dir
						if(this.File)				go.lFile				= this.File
						if(this.TextFile)		go.lTextFile		= this.TextFile
						if(this.Timer) 			go.lTimer				= this.Timer
						if(this.Data)				go.lData				= this.Data
						if(this.Data)				go.lBlob 				= this.Data.Blob
						if(this.DataSource)	go.lDataSource	= this.DataSource
						if(this.DataSource)	go.lDataSet			=	this.DataSource.DataSet
						if(this.Ui)					go.lUi					=	this.Ui
					}
				}

			return this
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
	//-------------------------------------------------------------------------------
	jsl.execute = function(script) {
		try {
			var v, s//!
			if(arguments.length !== 1)	throw jsl.ParamError()
			jsl.validate(script, "s", "script")//!

			if(script.length) {
				s = "var lconsole=jsl.console"

				if(jsl.Locale)			s += ",lLocale=jsl.Locale"
				if(jsl.Crypto)			s += ",lCrypto=jsl.Crypto"
				if(jsl.Storage)			s += ",lStorage=jsl.Storage"
				if(jsl.File)				s += ",lDir=jsl.Dir"
				if(jsl.File)				s += ",lFile=jsl.File"
				if(jsl.File)				s += ",lTextFile=jsl.TextFile"
				if(jsl.Timer) 			s += ",lTimer=jsl.Timer"
				if(jsl.Data)				s += ",lData=jsl.Data"
				if(jsl.Data)				s += ",lBlob=jsl.Data.Blob"
				if(jsl.DataSource)	s += ",lDataSource=jsl.DataSource"
				if(jsl.DataSource)	s += ",lDataSet=jsl.DataSource.DataSet"
				if(jsl.Ui)					s += ",lUi=jsl.Ui"

				v = this.__ni_jvs_exec(s + ";" + script)
			}
			return v
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
	//-------------------------------------------------------------------------------
	jsl.delay = function(action, time) {
		try {
      //!
			if(!arguments.length || arguments.length > 2) throw jsl.ParamError()
			jsl.validate(action, "f", "action")
			if(arguments.length === 2)	jsl.validate(time, "i+", "time")//!

			return jsl.__ni_tmr_create(action, arguments.length === 2 ? +time : 0, 1)
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
	//-------------------------------------------------------------------------------
	jsl.timer = function(action, interval, n) {
		try {
			//!
      if(arguments.length !== 2 && arguments.length !== 3) throw jsl.ParamError()
			jsl.validate(action		, "f"		, "action")
			jsl.validate(interval	, "i++"	, "interval")
			if(arguments.length === 3)	jsl.validate(n, "i++", "n")//!

			return jsl.__ni_tmr_create(action, +interval, arguments.length === 3 ? +n : Infinity)
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
	//-------------------------------------------------------------------------------
	jsl.removeTimer = function(tid) {
		try {
			//!
			if(arguments.length !== 1)	throw jsl.ParamError()
			jsl.validate(tid, "s+", "id")
      if(tid.charAt(0) !== "T")  	throw jsl.ParamError("BadId:tid:timer-id")//!

      jsl.__ni_tmr_remove(tid)
			return this
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
	//-------------------------------------------------------------------------------
	jsl.captureError = function(e, cx, args) {//!
		if(!arguments.length  || arguments.length > 3)	jsl.ParamError()//!

		try {
			var error, capture, evt

			if(arguments.length === 1)			error = this.Error(e)
			else if(arguments.length === 2)	error = this.Error(e).trace(cx)
			else														error = this.Error(e).trace(cx, args)

			evt = new jsl.Event
			evt.error	= error
			this.throwEvent("beforeError", evt)
			this.throwEvent("error", evt)
			return this
		}
		catch(e2) {
			//infinite recursion safe
			throw jsl.Error(e2, e).trace(this, arguments)
		}
	}
